define([
    'app',
    'clipboard',
    'json5',
    'jquery.format',
    'ace-language-tools'
], function(app, Clipboard, JSON5) {
    app.controller('HttpRequestController', function($scope, $http, $timeout, xDialog, xLocation, toastr,
            $cookies, $routeParams) {
        var self = this;
        self.url = '';
        self.method = 'GET';
        self.cookies = '';
        self.completed = true;
        self.activeTab = 0;

        self.dataType = '';
        self.dataTypes = ['form-data', 'x-www-form-urlencoded', 'raw', 'binary'];

        self.rawDataType = '';
        self.rawDataLabel = 'Text';
        self.rawDataTypes = [{
            mode: 'text', value: '', label: 'Text'
        }, {
            mode: 'text', value: 'text/plain', label: 'Text(text/plain)'
        }, {
            mode: 'hjson', value: 'application/json', label: 'JSON(application/json)'
        }, {
            mode: 'javascript', value: 'application/javascript', label: 'JavaScript(application/javascript)'
        }, {
            mode: 'xml', value: 'application/xml', label: 'XML(application/xml)'
        }, {
            mode: 'xml', value: 'text/xml', label: 'XML(text/xml)'
        }, {
            mode: 'html', value: 'text/html', label: 'HTML(text/html)'
        }];

        self.urlParsable = false;
        // parameters
        self.urlParams = [];
        self.headerParams = [];
        self.formParams = [];
        self.formDataParams = [];
        // response
        self.respActiveTab = 0;
        self.respBody = null;
        self.respHeaders = [];
        self.respCookies = [];

        var dataEditor = null, dataViewer = null;

        var dataParsable = true;
        self.dataParsable = true;

        var contentType = { name: 'Content-Type', checked: true, disabled: true };

        self.init = function() {
            // initial
            $timeout(function() {
                // data editor
                dataEditor = ace.edit('data-editor');
                dataEditor.getSession().setMode("ace/mode/text");
                dataEditor.setOptions({
                    fontSize: 12,
                    showPrintMargin: false,
                    autoScrollEditorIntoView: true,
                    enableBasicAutocompletion: true,
                    enableSnippets: true,
                    enableLiveAutocompletion: true
                });
                dataEditor.getSession().on('changeAnnotation', function(e) {
                    // check editor errors
                    $timeout(function() {
                        var annotations = dataEditor.getSession().getAnnotations();
                        dataParsable = true;
                        if (!_.isEmpty(annotations)) {
                            if (_.find(annotations, function(annotation) {
                                return annotation.type == 'error';
                            })) {
                                dataParsable = false;
                            }
                        }
                        if (self.dataParsable != dataParsable) {
                            self.dataParsable = dataParsable;
                        }
                    });
                });

                // data viewer
                dataViewer = ace.edit('data-viewer');
                dataViewer.getSession().setMode("ace/mode/text");
                dataViewer.setOptions({
                    fontSize: 12,
                    //wrap: true,
                    readOnly: true,
                    showGutter: false,
                    showLineNumbers: false,
                    showPrintMargin: false,
                    highlightActiveLine: false,
                    autoScrollEditorIntoView: true
                });

                new Clipboard('#copy', {
                    text: function() {
                        return dataViewer.getValue();
                    }
                }).on('success', function(e) {
                    toastr.success('已复制到剪贴板！');
                });


                $scope.$watch('vm.rawDataType', function(newValue) {
                    var selectedType = _.find(self.rawDataTypes, function(rawDataType) {
                        return rawDataType.value == newValue;
                    });
                    if (selectedType) {
                        var modeId = 'ace/mode/' + selectedType.mode;
                        if (modeId != dataEditor.getSession().getMode().$id) {
                            dataEditor.getSession().setMode(modeId);
                        }
                    }
                });

                $scope.$watch('vm.respBody', function(newValue) {
                    if (newValue == null) {
                        dataViewer.setValue('', -1);
                    } else {
                        var modeId = 'ace/mode/' + newValue.mode;
                        if (modeId != dataViewer.getSession().getMode().$id) {
                            dataViewer.getSession().setMode(modeId);
                        }
                        dataViewer.setValue(newValue.data, -1);
                    }
                    dataViewer.resize();
                });
            });

            // http preference scroller
            jQuery('.x-http').parents('.x-page-scope').scroll(function() {
                jQuery(this).find('.x-http-preference').css('top', this.scrollTop + 'px')
                    .css('left', this.scrollLeft + 'px');
            });

            $scope.$watch('vm.url', function(newValue) {
                self.urlParsable = /http(s)?:\/\/[\w-\.]+(:\d+)?(\/[\w-./?%&=]*)?(?:#(.*))?/.test(newValue);
            });

            $scope.$watchGroup(['vm.dataType', 'vm.rawDataType'], function(newValue) {
                switch(newValue[0]) {
                    case 'binary':
                    case 'form-data':
                        contentType.value = 'multipart/form-data';
                        break;
                    case 'x-www-form-urlencoded':
                        contentType.value = 'application/x-www-form-urlencoded';
                        break;
                    case 'raw':
                        contentType.value = newValue[1];
                        break;
                    default:
                        contentType.value = '';
                }
                var index = self.headerParams.indexOf(contentType);
                if (contentType.value == '') {
                    if (index != -1) {
                        self.headerParams.splice(index, 1);
                    }
                } else if (index == -1) {
                    self.headerParams.unshift(contentType);
                }
            });

            loadTestTarget();
        }

        function subMatch(string, regex) {
            var result = regex.exec(string);
            if (result != null && result.length >= 2) {
                return result[1];
            }
            return '';
        }

        function loadTestTarget() {
            if ($routeParams.target) {
                $http({
                    method: 'POST',
                    url: '/' + $routeParams.target + '?origin=true',
                    transformResponse: function(response) {
                        return response;
                    }
                }).then(function(response) {
                    var path = subMatch(response.data, /URL：(.*)/gi);
                    var host = $cookies.get('test_host');
                    if (_.isEmpty(host)) {
                        host = 'http://127.0.0.1:8080';
                    }
                    self.url = host + path;

                    self.method = subMatch(response.data, /Method：(get|post)/gi).toUpperCase();
                    self.method = (self.method == '' ? 'GET' : self.method);

                    var requestBody = subMatch(response.data, /入参：([\s\S]*)出参：/gm).trim();
                    if (!_.isEmpty(requestBody)) {
                        self.dataType = 'raw';
                        self.rawDataType = 'application/json';
                        self.rawDataLabel = 'JSON(application/json)';
                        $timeout(function() {
                            dataEditor.setValue(requestBody, -1);
                        }, 500);
                    }
                });
            }
        }

        self.reset = function() {
            self.url = '';
            self.method = 'GET';
            self.cookies = '';
            self.completed = true;
            self.activeTab = 0;

            self.dataType = '';
            self.rawDataType = '';
            self.rawDataLabel = 'Text';

            self.urlParsable = false;
            // parameters
            self.urlParams = [];
            self.headerParams = [{
                name: '',
                value: '',
                checked: true,
                disabled: false
            }];
            self.formParams = [{
                name: '',
                value: '',
                checked: true,
                disabled: false
            }];
            self.formDataParams = [{
                name: '',
                type: 'text',
                value: '',
                checked: true,
                disabled: false
            }];
            // response
            self.respActiveTab = 0;
            self.respBody = null;
            self.respHeaders = [];
            self.respCookies = [];

            dataEditor.setValue('');
            self.dataParsable = true;
        }

        self.editParams = function() {
            var location = xLocation.parse(self.url);
            self.urlParams = _.map(location.search(), function(value, name) {
                return { name: name, value: value };
            });
            xDialog.open().editParams({ params: self.urlParams }).then(function() {
                var params = {};
                _.forEach(self.urlParams, function(param) {
                    if (param.name != '') {
                        params[param.name] = param.value;
                    }
                });
                location.search(params);
                self.url = location.url();
            });
        }

        self.changeMethod = function(method) {
            if (method == 'GET' || method == 'HEAD') {
                self.activeTab = 0;
                self.dataParsable = true;
            } else {
                self.dataParsable = dataParsable;
            }
        }

        self.sendRequest = function(valid, type) {
            self.respActiveTab = 0;
            self.respBody = null;
            self.respHeaders = [];
            self.respCookies = [];
            // send request
            if (valid) {
                self.completed = false;

                if ($routeParams.target) {
                    var matches = self.url.match(/^http(s)?:\/\/[\w-\.]+(:\d+)?/);
                    if (!_.isEmpty(matches)) {
                        $cookies.put('test_host', matches[0]);
                    }
                }

                // create form
                var $form = jQuery('<form style="display:none">').attr({
                    'method': 'POST',
                    'accept-charset': 'UTF-8',
                    'enctype': 'multipart/form-data'
                });

                // hidden parameters
                _.forEach([
                    { name: '__url', value: self.url },
                    { name: '__method', value: self.method },
                    { name: '__headers', value: parseHeaders() },
                    { name: '__cookies', value: parseCookies() }
                ], function(param) {
                    $form.append(jQuery('<input type="hidden">').attr({
                        name: param.name,
                        value: param.value
                    }));
                });

                // body
                if (self.method != 'GET' && self.method != 'HEAD') {
                    switch(self.dataType) {
                        case 'form-data':
                        case 'x-www-form-urlencoded':
                            var fields = {};
                            var params = (self.dataType == 'form-data' ? self.formDataParams : self.formParams);
                            _.forEach(params, function(param) {
                                if (param.checked && param.name != '') {
                                    if (param.type == 'file') {
                                        $form.append(jQuery('#form-data input[name="' + param.name + '"]').clone());
                                    } else {
                                        fields[param.name] = param.value;
                                    }
                                }
                            });
                            $form.append(jQuery('<textarea name="__body">').val(JSON.stringify(fields)));
                            break;
                        case 'raw':
                            var body = dataEditor.getValue();
                            if (self.rawDataType == 'application/json' && body != '') {
                                body = JSON.stringify(JSON5.parse(body));
                            }
                            $form.append(jQuery('<textarea name="__body">').val(body));
                            break;
                        case 'binary':
                            $form.append(jQuery('#binary input[name="file"]').clone());
                            break;
                        default: break;
                    }
                }
                $form.appendTo(document.body);

                // send request
                if (type == 'download') {
                    var hasReleased = false;
                    var target = '__download_' + (new Date()).getTime();
                    var $downloadFrame = jQuery('<iframe style="display:none">') .attr('name', target)
                        .appendTo(document.body);
                    $downloadFrame.on('load', function() {
                        $timeout(function() {
                            hasReleased = true;
                            $form.remove();
                            var plainText = $downloadFrame.contents().find('body>pre').text();
                            self.respBody = {
                                mode: 'text',
                                data: (plainText ? plainText : $downloadFrame.contents().find('body').html())
                            };
                            $downloadFrame.remove();
                            self.completed = true;
                        });
                    });

                    $form.attr({
                        'action': '/mock/test/request',
                        'target': target
                    }).submit();
                    // release after 30s
                    $timeout(function() {
                        if (!hasReleased) {
                            self.completed = true;
                        }
                    }, 30000);
                } else {
                    $http({
                        url: '/mock/test/request',
                        method: 'POST',
                        headers: {
                            'Pragma': 'no-cache',
                            'Cache-Control': 'no-cache',
                            'Content-Type': undefined,
                        },
                        timeout: 60000,
                        data: new FormData($form[0]),
                        transformResponse: function(response) {
                            return response;
                        }
                    }).then(function(response) {
                        $form.remove();
                        self.completed = true;
                        self.respHeaders = buildHeaders(response.headers());
                        self.respCookies = buildCookies(response.headers('x-cookies'));

                        if (response.status >= 400 && response.data == '') {
                            xDialog.alert('错误信息', '服务器的返回信息：' + response.statusText);
                        } else {
                            var contentType = response.headers('Content-Type');
                            if (contentType != null) {
                                if (/\/json/.test(contentType)) {
                                    self.respBody = {
                                        mode: 'json',
                                        data: jQuery.format(response.data, { method: 'json' })
                                    };
                                } else if (/\/xml/.test(contentType)) {
                                    self.respBody = {
                                        mode: 'xml',
                                        data: jQuery.format(response.data, { method: 'xml' })
                                    };
                                } else {
                                    self.respBody = { mode: 'text', data: response.data };
                                }
                            } else {
                                self.respBody = { mode: 'text', data: response.data };
                            }
                        }
                    }, function(response) {
                        $form.remove();
                        self.completed = true;
                        self.respHeaders = buildHeaders(response.headers());
                        self.respCookies = buildCookies(response.headers('x-cookies'));
                    });
                }

                toastr.success('HTTP请求已发送！');
            }
        }

        function parseHeaders() {
            var headers = {};
            _.forEach(self.headerParams, function(param) {
                if (param.checked && param.name != '') {
                    headers[param.name] = param.value;
                }
            });
            return JSON.stringify(headers);
        }

        function parseCookies() {
            var cookies = {};
            if (self.cookies) {
                var cookie = self.cookies.trim().replace(/\n/g, ';'),
                    pattern = /([^=]+)=([^;]+);?\s*/g,
                    result = null;
                while ((result = pattern.exec(cookie)) != null) {
                    cookies[result[1]] = result[2];
                }
            }
            return JSON.stringify(cookies);
        }

        function buildHeaders(headers) {
            var params = {};
            _.forEach(headers, function(value, name) {
                if (name != 'x-cookies') {
                    params[name] = headers[name];
                }
            });
            return _.map(params, function(value, name) {
                return { name: name, value: value };
            });
        }

        function buildCookies(cookies) {
            return (cookies || '').split('___');
        }
    });
});
